home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994…tember: Reference Library / Dev.CD Sep 94.toast / Periodicals / develop / develop Issue 6 / develop 6 code / TCP / NewsWatcher / NewsWatcher 2.0d15 source / source / mark.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-21  |  21.5 KB  |  810 lines  |  [TEXT/KAHL]

  1. /*----------------------------------------------------------------------------
  2.  
  3.     mark.c
  4.  
  5.     This handles marking articles read and unread.
  6.     
  7.     Portions copyright © 1990, Apple Computer.
  8.     Portions copyright © 1993, Northwestern University.
  9.  
  10. ----------------------------------------------------------------------------*/
  11.  
  12. #include <string.h>
  13. #include <stdio.h>
  14.  
  15. #include "glob.h"
  16. #include "child.h"
  17. #include "mark.h"
  18. #include "nntp.h"
  19. #include "util.h"
  20. #include "wind.h"
  21.  
  22.  
  23. /*    FindParentCell locates the parent cell in a list window given an
  24.     index in a group or subject array for the window. */
  25.     
  26. Boolean FindParentCell (WindowPtr wind, short index, Cell *theCell)
  27. {
  28.     TWindow **info;
  29.     ListHandle theList;
  30.     short numCells, i;
  31.     short *pCells;
  32.  
  33.     info = (TWindow**)GetWRefCon(wind);
  34.     theList = (**info).theList;
  35.     numCells = (**theList).dataBounds.bottom;
  36.     pCells = (short*)*((**theList).cells);
  37.     for (i = 0; i < numCells; i++) {
  38.         if (*pCells == index) {
  39.             SetPt(theCell, 0, i);
  40.             return true;
  41.         }
  42.         pCells++;
  43.     }
  44.     return false;
  45. }
  46.  
  47.  
  48. /*    AppendUnreadRange appends a range of unread messages to the end of the 
  49.     unread list for a group. */
  50.  
  51. void AppendUnreadRange (long first, long last, TGroup *theGroup)
  52. {
  53.     TUnread **unread, **lastRec, **nextRec;
  54.  
  55.     lastRec = theGroup->unread;
  56.     if (lastRec != nil) {
  57.         while (true) {
  58.             nextRec = (**lastRec).next;
  59.             if (nextRec == nil) break;
  60.             lastRec = nextRec;
  61.         }
  62.     }
  63.     
  64.     if (lastRec != nil && (**lastRec).lastUnread + 1 == first) {
  65.         (**lastRec).lastUnread = last;
  66.     } else {
  67.         unread = (TUnread**)MyNewHandle(sizeof(TUnread));
  68.         if (MyMemErr() != noErr)
  69.             return;
  70.         (**unread).firstUnread = first;
  71.         (**unread).lastUnread = last;
  72.         (**unread).next = nil;
  73.         if (lastRec == nil) {
  74.             theGroup->unread = unread;
  75.         } else {
  76.             (**lastRec).next = unread;
  77.         }
  78.     }
  79.     theGroup->numUnread += last - first + 1;
  80. }
  81.  
  82.  
  83.  
  84. /*----------------------------------------------------------------------------
  85.  * AdjustUnreadList adjusts the unread list for a group. It trims the unread
  86.  * list to include just articles in the range [firstMess, lastMess].
  87.  * It also recomputes numUnread.
  88.  *
  89.  * Entry:    theGroup = pointer to group record.
  90.  */
  91.  
  92. void AdjustUnreadList (TGroup *theGroup)
  93. {
  94.     TUnread **prev, **cur;
  95.     
  96.     /* Delete all unread ranges which come before firstMess. */
  97.     
  98.     cur = theGroup->unread;
  99.     while (cur && (**cur).lastUnread < theGroup->firstMess) {
  100.         prev = cur;
  101.         cur = (**cur).next;
  102.         MyDisposHandle((Handle)prev);
  103.     }
  104.     theGroup->unread = cur;
  105.     
  106.     /* If necessary, trim the first unread range to begin at firstMess. */
  107.     
  108.     if (cur && (**cur).firstUnread < theGroup->firstMess) 
  109.         (**cur).firstUnread = theGroup->firstMess;
  110.     
  111.     /* Leave alone all unread ranges which come before lastMess. */    
  112.     
  113.     prev = nil;
  114.     while (cur && (**cur).firstUnread <= theGroup->lastMess) {
  115.         prev = cur;
  116.         cur = (**cur).next; 
  117.     }
  118.     
  119.     /* If necessary, trim the last unread range to end at lastMess. */
  120.     
  121.     if (prev && (**prev).lastUnread > theGroup->lastMess)
  122.         (**prev).lastUnread = theGroup->lastMess;
  123.     if (prev) {
  124.         (**prev).next = nil;
  125.     } else {
  126.         theGroup->unread = nil;
  127.     }
  128.         
  129.     /* Delete the remaining unread ranges, which all come after lastMess. */
  130.         
  131.     while (cur) {
  132.         prev = cur;
  133.         cur = (**cur).next;
  134.         MyDisposHandle((Handle)prev);
  135.     }
  136.     
  137.     /* Recalculate numUnread. */
  138.     
  139.     theGroup->numUnread = 0;
  140.     cur = theGroup->unread;
  141.     while (cur) {
  142.         theGroup->numUnread += (**cur).lastUnread - (**cur).firstUnread + 1;
  143.         cur = (**cur).next;
  144.     }
  145. }
  146.  
  147.  
  148. /*    UpdateUnreadList is called to update the unread message list for a group
  149.     in a user group list which has an open subject window.
  150.     
  151.     Entry:    wind = pointer to subject window for group.
  152. */
  153.  
  154. void UpdateUnreadList (WindowPtr wind)
  155. {
  156.     long tmpFirst,tmpLast,i;
  157.     TGroup theGroup,**groupArray;
  158.     TSubject **subjectArray;
  159.     TWindow **info,**parentInfo;
  160.     short index, numSubjects;
  161.     WindowPtr parentWindow;
  162.     long firstFetched;
  163.     TUnread **unread, **prevUnread=nil, **nextUnread;
  164.     
  165.     info = (TWindow**)GetWRefCon(wind);
  166.     parentWindow = (**info).parentWindow;
  167.     parentInfo = (TWindow**)GetWRefCon(parentWindow);
  168.     if ((**parentInfo).kind != kUserGroup) return;
  169.     groupArray = (**parentInfo).groupArray;
  170.     index = (**info).parentGroup;
  171.     theGroup = (*groupArray)[index];
  172.     
  173.     /* Truncate the unread list for the group so that the unread list
  174.        includes only articles which were NOT included in the subject
  175.        window (because there were more than gPrefs.maxFetch unread
  176.        articles when the subject window was first opened). */
  177.     
  178.     firstFetched = (**info).firstFetched;
  179.     unread = theGroup.unread;
  180.     theGroup.numUnread = 0;
  181.     while (unread != nil) {
  182.         if (firstFetched <= (**unread).lastUnread) break;
  183.         theGroup.numUnread += (**unread).lastUnread - (**unread).firstUnread + 1;
  184.         prevUnread = unread;
  185.         unread = (**unread).next;
  186.     }
  187.     if (unread != nil) {
  188.         if ((**unread).firstUnread < firstFetched) {
  189.             (**unread).lastUnread = firstFetched-1;
  190.             theGroup.numUnread += firstFetched - (**unread).firstUnread;
  191.             prevUnread = unread;
  192.             unread = (**unread).next;
  193.             (**prevUnread).next = nil;
  194.         } else {
  195.             if (prevUnread == nil) {
  196.                 theGroup.unread = nil;
  197.             } else {
  198.                 (**prevUnread).next = nil;
  199.             }
  200.         }
  201.         while (unread != nil) {
  202.             nextUnread = (**unread).next;
  203.             MyDisposHandle((Handle)unread);
  204.             unread = nextUnread;
  205.         }
  206.     }
  207.     
  208.     (**parentInfo).changed = true;
  209.  
  210.     /* Walk the subject array for the subject window and append new unread
  211.        article ranges to the end of the unread list for the group. */
  212.     
  213.     subjectArray = (**info).subjectArray;
  214.     numSubjects = (**info).numSubjects;
  215.     
  216.     tmpFirst = -1;
  217.         
  218.     for (i=0; i<numSubjects; i++) {            
  219.         if (!(*subjectArray)[i].read && 
  220.             i < numSubjects-1 && 
  221.             (*subjectArray)[i+1].number-(*subjectArray)[i].number > 1) 
  222.         {
  223.             if (tmpFirst == -1) tmpFirst = (*subjectArray)[i].number;
  224.             AppendUnreadRange(tmpFirst, (*subjectArray)[i].number, &theGroup);
  225.             tmpFirst = -1;
  226.         }
  227.         else if (tmpFirst == -1 && !(*subjectArray)[i].read)
  228.             tmpFirst = tmpLast = (*subjectArray)[i].number;
  229.         else if (tmpFirst != -1 && !(*subjectArray)[i].read)
  230.             tmpLast = (*subjectArray)[i].number;
  231.         else if (tmpFirst != -1 && (*subjectArray)[i].read) {
  232.             AppendUnreadRange(tmpFirst, tmpLast, &theGroup);
  233.             tmpFirst = -1;
  234.         }
  235.     }
  236.     if (tmpFirst != -1) AppendUnreadRange(tmpFirst, tmpLast, &theGroup);
  237.         
  238.     (*groupArray)[index] = theGroup;
  239. }
  240.  
  241.  
  242. /*    LoMarkGroup adjusts the number of unread articles in a group list window
  243.     when one or more subjects in a child subject list window have
  244.     been marked read or unread. 
  245. */
  246.     
  247. static void LoMarkGroup (WindowPtr child, short deltaUnread)
  248. {
  249.     WindowPtr parent;
  250.     TWindow **childInfo, **parentInfo;
  251.     TGroup **parentGroupArray;
  252.     ListHandle parentList;
  253.     long numUnread;
  254.     short index;
  255.     Cell theCell;
  256.     
  257.     if (deltaUnread == 0) return;
  258.     childInfo = (TWindow**)GetWRefCon(child);
  259.     parent = (**childInfo).parentWindow;
  260.     parentInfo = (TWindow**)GetWRefCon(parent);
  261.     if ((**parentInfo).kind != kUserGroup) return;
  262.     parentGroupArray = (**parentInfo).groupArray;
  263.     parentList = (**parentInfo).theList;
  264.     index = (**childInfo).parentGroup;
  265.     if (FindParentCell(parent, index, &theCell)) {
  266.         numUnread = (*parentGroupArray)[index].numUnread;
  267.         numUnread += deltaUnread;
  268.         if (numUnread < 0) numUnread = 0;
  269.         (*parentGroupArray)[index].numUnread = numUnread;
  270.         (*parentGroupArray)[index].onlyRedrawCount = true;
  271.         LDraw(theCell, parentList);
  272.         (*parentGroupArray)[index].onlyRedrawCount = false;
  273.     }
  274. }
  275.  
  276.  
  277. /*    LoMarkArticle marks an article window read or unread. */
  278.  
  279. static void LoMarkArticle (WindowPtr wind, Boolean read)
  280. {
  281.     Str255 title;
  282.  
  283.     GetWTitle(wind,title);
  284.     if (read) {
  285.         if (title[1] == (unsigned char)'√') return;
  286.         if (*title == 255) *title == 254;
  287.         BlockMove(title+1, title+2, *title);
  288.         title[1] = '√';
  289.         (*title)++;
  290.     } else {
  291.         if (title[1] != (unsigned char)'√') return;
  292.         (*title)--;
  293.         BlockMove(title+2, title+1, *title);
  294.     }
  295.     RemoveWindMenu(wind);
  296.     SetWTitle(wind,title);
  297.     AddWindMenu(wind);
  298. }
  299.  
  300.  
  301. /*    LoMarkSubject marks a single subject cell in a subject window read 
  302.     or unread. Any open child window of the cell is also marked. If
  303.     the cell is collapsed, all the articles in the thread are marked, as
  304.     are any open children.
  305. */
  306.  
  307. static void LoMarkSubject (WindowPtr wind, Cell theCell, Boolean read,
  308.     short *numMarked)
  309. {
  310.     WindowPtr child;
  311.     TWindow **info;
  312.     ListHandle theList;
  313.     TSubject **subjectArray;
  314.     short cellData, cellDataLen, nextInThread;
  315.  
  316.     *numMarked = 0;
  317.     info = (TWindow**)GetWRefCon(wind);
  318.     theList = (**info).theList;
  319.     subjectArray = (**info).subjectArray;
  320.     cellDataLen = 2;
  321.     LGetCell(&cellData, &cellDataLen, theCell, theList);
  322.     if ((*subjectArray)[cellData].read != read) {
  323.         (*subjectArray)[cellData].read = read;
  324.         (*numMarked)++;
  325.     }
  326.     child = FindChildByCellData(wind, cellData);
  327.     if (child != nil) LoMarkArticle(child, read);
  328.     if ((*subjectArray)[cellData].collapsed) {
  329.         nextInThread = (*subjectArray)[cellData].nextInThread;
  330.         while (nextInThread != -1) {
  331.             if ((*subjectArray)[nextInThread].read != read) {
  332.                 (*subjectArray)[nextInThread].read = read;
  333.                 (*numMarked)++;
  334.             }
  335.             child = FindChildByCellData(wind, nextInThread);
  336.             if (child != nil) LoMarkArticle(child, read);
  337.             nextInThread = (*subjectArray)[nextInThread].nextInThread;
  338.         }
  339.     }
  340.     (*subjectArray)[cellData].onlyRedrawCheck = true;
  341.     LDraw(theCell, theList);
  342.     (*subjectArray)[cellData].onlyRedrawCheck = false;
  343. }
  344.  
  345.  
  346. /*    LoMarkOneRead marks a single cross-referenced message in a specified group 
  347.     as being read.
  348. */
  349.  
  350. static void LoMarkOneRead (TGroup *theGroup, long number)
  351. {
  352.     TUnread **unread,**newUnread,**prevUnread;
  353.         
  354.     for (unread = prevUnread = theGroup->unread;
  355.         unread && !(number>=(**unread).firstUnread && 
  356.             number<=(**unread).lastUnread);
  357.         prevUnread = unread,unread = (**unread).next);
  358.     
  359.     if (!unread) return; /* already read */
  360.     
  361.     if (number == (**unread).firstUnread || number == (**unread).lastUnread) {
  362.         if (number == (**unread).firstUnread) {
  363.             (**unread).firstUnread++;
  364.         } else {
  365.             (**unread).lastUnread--;
  366.         }
  367.         if ((**unread).firstUnread > (**unread).lastUnread) {
  368.             if (unread != prevUnread) {
  369.                 (**prevUnread).next = (**unread).next;
  370.             } else {
  371.                 theGroup->unread = (**unread).next;
  372.             }
  373.             MyDisposHandle((Handle)unread);
  374.         }
  375.     } else {
  376.         /* split this one in half */
  377.         newUnread = (TUnread**)MyNewHandle(sizeof(TUnread));
  378.         (**newUnread).next = (**unread).next;
  379.         (**unread).next = newUnread;
  380.         (**newUnread).lastUnread = (**unread).lastUnread;
  381.         (**newUnread).firstUnread = number + 1;
  382.         (**unread).lastUnread = number - 1;
  383.     }
  384.     theGroup->numUnread -= 1;
  385. }
  386.  
  387.  
  388. /*    LoMarkOneUnread marks a single cross-referenced message in a specified group 
  389.     as being unread.
  390. */
  391.  
  392. static void LoMarkOneUnread (TGroup *theGroup, long number)
  393. {
  394.     TUnread **unread,**newUnread,**prevUnread;
  395.         
  396.     for (unread = prevUnread = theGroup->unread;
  397.         unread && number>(**unread).lastUnread;
  398.         prevUnread = unread,unread = (**unread).next);
  399.     
  400.     if (unread != nil && number>=(**unread).firstUnread) return; /* already unread */
  401.     
  402.     if (unread == nil) {
  403.         /* insert at end of list, after prevUnread. */
  404.         if (prevUnread != nil && (**prevUnread).lastUnread == number-1) {
  405.             (**prevUnread).lastUnread++;
  406.         } else {
  407.             newUnread = (TUnread**)MyNewHandle(sizeof(TUnread));
  408.             (**newUnread).firstUnread = (**newUnread).lastUnread = number;
  409.             (**newUnread).next = nil;
  410.             if (prevUnread == nil) {
  411.                 theGroup->unread = newUnread;
  412.             } else {
  413.                 (**prevUnread).next = newUnread;
  414.             }
  415.         }
  416.     } else if (unread == prevUnread) {
  417.         /* insert at beginning of list, before unread. */
  418.         if ((**unread).firstUnread == number+1) {
  419.             (**unread).firstUnread--;
  420.         } else {
  421.             newUnread = (TUnread**)MyNewHandle(sizeof(TUnread));
  422.             (**newUnread).firstUnread = (**newUnread).lastUnread = number;
  423.             (**newUnread).next = unread;
  424.             theGroup->unread = newUnread;
  425.         }
  426.     } else {
  427.         /* insert in middle of list, between prevUnread and unread. */
  428.         if ((**prevUnread).lastUnread == number-1 || 
  429.             (**unread).firstUnread == number+1) 
  430.         {
  431.             if ((**prevUnread).lastUnread == number-1) {
  432.                 (**prevUnread).lastUnread++;
  433.             } else {
  434.                 (**unread).firstUnread--;
  435.             }
  436.             if ((**prevUnread).lastUnread >= (**unread).firstUnread - 1) {
  437.                 /* join two blocks together */
  438.                 (**prevUnread).lastUnread = (**unread).lastUnread;
  439.                 (**prevUnread).next = (**unread).next;
  440.                 MyDisposHandle((Handle)unread);
  441.             }
  442.         } else {
  443.             newUnread = (TUnread**)MyNewHandle(sizeof(TUnread));
  444.             (**newUnread).firstUnread = (**newUnread).lastUnread = number;
  445.             (**prevUnread).next = newUnread;
  446.             (**newUnread).next = unread;
  447.         }
  448.     }
  449.     theGroup->numUnread += 1;
  450. }
  451.  
  452.  
  453. /*    MarkOne marks a single cross-referenced message in a specified group as 
  454.     being read or unread. */
  455.  
  456. static void MarkOne (char *groupName, long number, WindowPtr wind, 
  457.     Boolean read)
  458. {
  459.     TWindow **info, **childInfo;
  460.     TGroup theGroup, **groupArray;
  461.     ListHandle parentList, childList;
  462.     TSubject **subjectArray, *pSubject;
  463.     Cell parentCell, childCell;
  464.     short parentCellData, childIndex, cellDataLen, numCells, numSubjects;
  465.     short threadHeadIndex;
  466.     Handle strings;
  467.     WindowPtr child, grandChild;
  468.     
  469.     info = (TWindow**)GetWRefCon(wind);
  470.     parentList = (**info).theList;
  471.     groupArray = (**info).groupArray;
  472.     strings = (**info).strings;
  473.     numCells = (**parentList).dataBounds.bottom;
  474.     parentCell.h = 0;
  475.     for (parentCell.v = 0; parentCell.v < numCells; parentCell.v++) {
  476.         cellDataLen = 2;
  477.         LGetCell(&parentCellData, &cellDataLen, parentCell, parentList);
  478.         if (strcmp(*strings + (*groupArray)[parentCellData].nameOffset, 
  479.             groupName) == 0) break;
  480.     }
  481.     if (parentCell.v >= numCells) return;
  482.     child = FindChild(wind, parentCell);
  483.     if (child != nil) {
  484.         childInfo = (TWindow**)GetWRefCon(child);
  485.         childList = (**childInfo).theList;
  486.         subjectArray = (**childInfo).subjectArray;
  487.         numSubjects = (**childInfo).numSubjects;
  488.         pSubject = *subjectArray;
  489.         for (childIndex = 0; childIndex < numSubjects; childIndex++) {
  490.             if (pSubject->number == number) break;
  491.             pSubject++;
  492.         }
  493.         if (childIndex >= numSubjects) return;
  494.         threadHeadIndex = (*subjectArray)[childIndex].threadHeadIndex;
  495.         if ((*subjectArray)[childIndex].read != read) {
  496.             if (!FindParentCell(child, threadHeadIndex, &childCell)) return;
  497.             (*subjectArray)[childIndex].read = read;
  498.             (*subjectArray)[threadHeadIndex].onlyRedrawCheck = true;
  499.             LDraw(childCell, childList);
  500.             (*subjectArray)[threadHeadIndex].onlyRedrawCheck = false;
  501.             (*groupArray)[parentCellData].numUnread += read ? -1 : 1;
  502.             (*groupArray)[parentCellData].onlyRedrawCount = true;
  503.             LDraw(parentCell, parentList);
  504.             (*groupArray)[parentCellData].onlyRedrawCount = false;
  505.             grandChild = FindChildByCellData(child, childIndex);
  506.             if (grandChild != nil) LoMarkArticle(grandChild, read);
  507.         }
  508.     } else {
  509.         theGroup = (*groupArray)[parentCellData];
  510.         if (read) {
  511.             LoMarkOneRead(&theGroup, number);
  512.         } else {
  513.             LoMarkOneUnread(&theGroup, number);
  514.         }
  515.         (*groupArray)[parentCellData] = theGroup;
  516.         (*groupArray)[parentCellData].onlyRedrawCount = true;
  517.         LDraw(parentCell, parentList);
  518.         (*groupArray)[parentCellData].onlyRedrawCount = false;
  519.     }
  520. }
  521.  
  522.  
  523. /*
  524.     MarkXrefs marks articles which appear in multiple newsgroups as read
  525.     or unread in all of the newsgroups.
  526. */
  527.  
  528. static void MarkXrefs (WindowPtr wind, Boolean read)
  529. {
  530.     TWindow **info,**parentInfo,**grandParentInfo;
  531.     WindowPtr parent,grandParent;
  532.     long offset,endHeader,endLine;
  533.     Handle theText;
  534.     char *current,*store;
  535.     CStr255 xrefGroup, xrefNumber, mungeText;
  536.     long number;
  537.     
  538.     info = (TWindow**)GetWRefCon(wind);
  539.     if ((**info).kind != kArticle) return;
  540.     parent = (**info).parentWindow;
  541.     if (parent == nil) return;
  542.     parentInfo = (TWindow**)GetWRefCon(parent);
  543.     if ((**parentInfo).kind != kSubject) return;
  544.     grandParent = (**parentInfo).parentWindow;
  545.     if (grandParent == nil) return;
  546.     grandParentInfo = (TWindow**)GetWRefCon(grandParent);
  547.     if ((**grandParentInfo).kind != kUserGroup) return;
  548.     
  549.     theText = (**info).fullText;
  550.     
  551.     strcpy(mungeText,CRSTR);
  552.     strcat(mungeText,"Xref:");
  553.  
  554.     offset = Munger(theText,0,mungeText,6,nil,0);
  555.     if (offset < 0) {
  556.         offset = Munger(theText,0,"Xref:",5,nil,0);
  557.         if (offset != 0) offset = -1;
  558.     }
  559.     if (offset < 0) return;
  560.     
  561.     endHeader = Munger(theText,0,CRCR,2,nil,0);
  562.     if (offset > endHeader) return;
  563.     
  564.     endLine = Munger(theText,offset+1,CRSTR,1,nil,0);
  565.     
  566.     HLock(theText);
  567.     current = (*theText)+offset;
  568.     
  569.     /* skip over site name */
  570.     
  571.     current += 6;
  572.     while (*current == ' ' && *current != CR)
  573.         current++;
  574.     while (*current != ' ' && *current != CR)
  575.         current++;
  576.     
  577.     /* parse xrefed groups */
  578.     
  579.     while (current < (*theText)+endLine) {
  580.         store = xrefGroup;
  581.         while (*current == ' ' && *current != CR)
  582.             current++;
  583.         while (*current != ':' && *current != ' ' && *current != CR) {
  584.             *store++ = *current++;
  585.         }
  586.         current++;
  587.         *store = 0;
  588.         
  589.         store = xrefNumber;
  590.         while (*current != ' ' && *current != ' ' && *current != CR) 
  591.             *store++ = *current++;
  592.         *store = 0;
  593.         c2pstr(xrefNumber);
  594.         StringToNum((StringPtr)xrefNumber,&number);
  595.         MarkOne(xrefGroup,number,grandParent,read);
  596.     }
  597.     
  598.     HUnlock(theText);
  599. }
  600.  
  601.  
  602. /*    MarkSelectedSubjects marks all selected subjects in a subject window 
  603.     read or unread.
  604. */
  605.  
  606. static void MarkSelectedSubjects (WindowPtr wind, Boolean read)
  607. {
  608.     TWindow **info;
  609.     Cell theCell;
  610.     short totMarked = 0, numMarked;
  611.     
  612.     info = (TWindow**)GetWRefCon(wind);
  613.     if ((**info).kind != kSubject) return;
  614.  
  615.     SetPt(&theCell, 0, 0);
  616.     while (LGetSelect(true, &theCell, (**info).theList)) {
  617.         LoMarkSubject(wind, theCell, read, &numMarked);
  618.         theCell.v++;
  619.         totMarked += numMarked;
  620.     }
  621.     LoMarkGroup(wind, read ? -totMarked : totMarked);
  622. }
  623.  
  624.  
  625. /*    MarkAllSubjects marks all subjects in a subject window read or unread.
  626. */
  627.  
  628. void MarkAllSubjects (WindowPtr wind, Boolean read)
  629. {
  630.     TWindow **info;
  631.     Cell theCell;
  632.     ListHandle theList;
  633.     short numCells;
  634.     short totMarked = 0, numMarked;
  635.     
  636.     info = (TWindow**)GetWRefCon(wind);
  637.     theList = (**info).theList;
  638.     numCells = (**theList).dataBounds.bottom;
  639.     theCell.h = 0;
  640.     for (theCell.v = 0; theCell.v < numCells; theCell.v++) {
  641.         LoMarkSubject(wind, theCell, read, &numMarked);
  642.         totMarked += numMarked;
  643.     }
  644.     LoMarkGroup(wind, read ? -totMarked : totMarked);
  645. }
  646.  
  647.  
  648. /*    MarkArticle marks an article window read or unread.
  649. */
  650.  
  651. void MarkArticle (WindowPtr wind, Boolean read)
  652. {
  653.     WindowPtr parent;
  654.     TWindow **info,**parentInfo;
  655.     Cell theCell;
  656.     ListHandle parentList;
  657.     TSubject **subjectArray;
  658.     short index, threadHeadIndex;
  659.     
  660.     info = (TWindow**)GetWRefCon(wind);
  661.     index = (**info).parentSubject;
  662.     parent = (**info).parentWindow;
  663.     parentInfo = (TWindow**)GetWRefCon(parent);
  664.     subjectArray = (**parentInfo).subjectArray;
  665.     parentList = (**parentInfo).theList;
  666.         
  667.     LoMarkArticle(wind, read);
  668.     
  669.     if ((*subjectArray)[index].read != read) {
  670.         (*subjectArray)[index].read = read;
  671.         threadHeadIndex = (*subjectArray)[index].threadHeadIndex;
  672.         if (FindParentCell(parent, threadHeadIndex, &theCell)) {
  673.             (*subjectArray)[threadHeadIndex].onlyRedrawCheck = true;
  674.             LDraw(theCell, parentList);
  675.             (*subjectArray)[threadHeadIndex].onlyRedrawCheck = false;
  676.         }
  677.         LoMarkGroup(parent, read ? -1 : +1);
  678.     }
  679.     
  680.     MarkXrefs(wind, read);
  681. }
  682.  
  683.  
  684. /*    MarkSelectedGroups marks all selected groups in a group window read or 
  685.     unread.
  686. */
  687.  
  688. static void MarkSelectedGroups (WindowPtr wind, Boolean read)
  689. {
  690.     WindowPtr child;
  691.     TWindow **info;
  692.     Cell theCell;
  693.     short cellData, cellDataLen;
  694.     TGroup **groupArray,theGroup;
  695.     ListHandle theList;
  696.     TUnread **pUnreadRec,**qUnreadRec;
  697.     
  698.     info = (TWindow**)GetWRefCon(wind);
  699.     if ((**info).kind != kUserGroup) return;
  700.     theList = (**info).theList;
  701.     groupArray = (**info).groupArray;
  702.         
  703.     SetPt(&theCell,0,0);
  704.     while (LGetSelect(true, &theCell, theList)) {
  705.     
  706.         cellDataLen = 2;
  707.         LGetCell(&cellData, &cellDataLen, theCell, theList);
  708.         
  709.         theGroup = (*groupArray)[cellData];
  710.         
  711.         pUnreadRec = theGroup.unread;
  712.         while (pUnreadRec) {
  713.             qUnreadRec = (**pUnreadRec).next;
  714.             MyDisposHandle((Handle)pUnreadRec);
  715.             pUnreadRec = qUnreadRec;
  716.         }
  717.         if (read) {
  718.             theGroup.unread = nil;
  719.             theGroup.numUnread = 0;
  720.         } else {
  721.             pUnreadRec = (TUnread**)MyNewHandle(sizeof(TUnread));
  722.             (**pUnreadRec).firstUnread = theGroup.firstMess;
  723.             (**pUnreadRec).lastUnread = theGroup.lastMess;
  724.             (**pUnreadRec).next = nil;
  725.             theGroup.unread = pUnreadRec;
  726.             theGroup.numUnread = theGroup.lastMess - theGroup.firstMess + 1;
  727.         }
  728.         
  729.         (*groupArray)[cellData] = theGroup;
  730.         
  731.         (*groupArray)[cellData].onlyRedrawCount = true;
  732.         LDraw(theCell, theList);
  733.         (*groupArray)[cellData].onlyRedrawCount = false;
  734.  
  735.         child = FindChild(wind, theCell);
  736.         if (child != nil) MarkAllSubjects(child, read);
  737.         
  738.         (**info).changed = true;
  739.         
  740.         theCell.v++;
  741.     }
  742. }
  743.  
  744.  
  745. /*    MarkThread marks all the articles in a thread read or unread.
  746. */
  747.  
  748. void MarkThread (WindowPtr wind, short threadHeadIndex, Boolean read)
  749. {
  750.     TWindow **info;
  751.     TSubject **subjectArray;
  752.     WindowPtr child;
  753.     short index, numMarked = 0;
  754.     Boolean collapsed;
  755.     Cell theCell;
  756.     ListHandle theList;
  757.     
  758.     info = (TWindow**)GetWRefCon(wind);
  759.     theList = (**info).theList;
  760.     subjectArray = (**info).subjectArray;
  761.     collapsed = (*subjectArray)[threadHeadIndex].collapsed;
  762.     if (!FindParentCell(wind, threadHeadIndex, &theCell)) return;
  763.     index = threadHeadIndex;
  764.     while (index != -1) {
  765.         if ((*subjectArray)[index].read != read) {
  766.             (*subjectArray)[index].read = read;
  767.             numMarked++;
  768.             child = FindChildByCellData(wind, index);
  769.             if (child != nil) LoMarkArticle(child, read);
  770.             if (!collapsed) {
  771.                 (*subjectArray)[index].onlyRedrawCheck = true;
  772.                 LDraw(theCell, theList);
  773.                 (*subjectArray)[index].onlyRedrawCheck = false;
  774.                 theCell.v++;
  775.             }
  776.         }
  777.         index = (*subjectArray)[index].nextInThread;
  778.     }
  779.     if (collapsed) {
  780.         (*subjectArray)[threadHeadIndex].onlyRedrawCheck = true;
  781.         LDraw(theCell, theList);
  782.         (*subjectArray)[threadHeadIndex].onlyRedrawCheck = false;
  783.     }
  784.  
  785.     LoMarkGroup(wind, read ? -numMarked : numMarked);
  786. }
  787.  
  788.  
  789. /*    DoMarkCommand handles the Mark Read and Mark Unread commands. */
  790.  
  791. void DoMarkCommand (WindowPtr wind, Boolean read)
  792. {
  793.     TWindow **info;
  794.     
  795.     info = (TWindow**)GetWRefCon(wind);
  796.  
  797.     switch ((**info).kind) {
  798.         case kUserGroup:
  799.             MarkSelectedGroups(wind, read);
  800.             break;
  801.         case kSubject:
  802.             MarkSelectedSubjects(wind, read);
  803.             break;
  804.         case kArticle:
  805.             MarkArticle(wind, read);
  806.             break;
  807.     }
  808. }
  809.  
  810.